/*
 *  Copyright 2008 Mark Ashworth <javameme@gmail.com>.
 * 
 *  Licensed under the GNU General Public License v3 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.gnu.org/licenses/gpl-3.0.html
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package xperiment.metaphor.model.specification;

import xperiment.metaphor.model.reflection.ReflectionError;
import java.lang.reflect.Method;
import java.util.List;

/**
 * A specification that determines that all the properties are equal. This could
 * be used for example to check that the <i>password</i> and <i>confirm password</i>
 * are equals when a user changes their password.<br />
 * 
 * @author Mark Ashworth <javameme@gmail.com>
 * @version 1.0.0
 */
public class PropertiesEqualSpecification extends AbstractSpecification {

    private String failureMessage;
    private String[] propertyNames;
    
    /**
     * Constructs the specification with the properties to test and the
     * failure message.
     */
    public PropertiesEqualSpecification(String[] propertyNames, String failureMessage) {
        super();
        if (propertyNames == null) {
            throw new IllegalArgumentException("The propertyNames is null");
        } else if (propertyNames.length < 2) {
            throw new IllegalArgumentException("The propertyNames must contain atleast two properties");
        }
        if (failureMessage == null) {
            throw new IllegalArgumentException("The failure message is null");
        }
        
        this.propertyNames = propertyNames;
        this.failureMessage = failureMessage;
    }
    
    public boolean isSatisfiedBy(Object object, List<SpecificationError> errors) {
        
        if (object == null) {
            errors = add(errors, new SpecificationError("object", "The object is null"));
            return false;
        }
        
        Object[] values = new Object[propertyNames.length];
        for(int i = 0; i < propertyNames.length; i++) {
            String name = propertyNames[i];
            if (!name.startsWith("get")) {
                name = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
            }
                    
            try {
                Method method = object.getClass().getMethod(name, new Class[0]);
                values[i] = method.invoke(object, new Object[0]);
            } catch (NoSuchMethodException e) {
                errors = add(
                        errors, 
                        new SpecificationError(
                            object.getClass().getName(), 
                            new String[] {ReflectionError.METHOD_DOES_NOT_EXISTS.getMessageCode()},
                            new Object[] {name},
                            e.getMessage()));
            } catch (Exception e) {
                errors = add(
                        errors, 
                        new SpecificationError(
                            object.getClass().getName(), 
                            new String[] {ReflectionError.INVOKE_ERROR.getMessageCode()},
                            new Object[]{},
                            e.getMessage()));
            }
        }
        
        boolean result = false;
        if (values.length == 2) {
            result = values[0].equals(values[1]);
        } else {
            result = true;
            for (int i = 0; i < values.length; i++) {
                for (int j = 0; j < values.length; j++) {
                    if (!values[i].equals(values[j])) {
                        result = false;
                    }
                }
            }
        }
        
        if (!result) {
            errors = add(errors, new SpecificationError(object.getClass().getName(), failureMessage));
        }
        return result;
    }
}
